# -- encoding: utf-8 --
#!/usr/bin/env python2

# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

import atexit
import getopt
import gettext
import gobject
import gtk
import os
import pygtk
import subprocess
import sys
import xdg.BaseDirectory as base

from pkg_resources import *
from distutils.version import LooseVersion as LVer

pygtk.require('2.0')
sys.path.append(os.path.abspath('..'))

GETTEXT_DOMAIN= "etude"

#set directories
if os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)),'plugins-devel')):
    _source_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    CONFIG_DIR = os.path.join(_source_dir, 'config')
    LOG_DIR = os.path.join(_source_dir, 'log')

    PLUGIN_DIR = [os.path.join(_source_dir, 'etude', 'plugins-devel'),]
    CACHE_DIR = os.path.join(_source_dir, 'cache')
    LOCALE_DIR = os.path.join(_source_dir, 'locale')
else: #normal start
    PLUGIN_DIR = [os.path.join(os.path.dirname(os.path.abspath(__file__)), 'plugins'),]
    PLUGIN_DIR.append(os.path.join(base.xdg_data_home, 'etude/plugins'))
    CONFIG_DIR = os.path.join(base.xdg_config_home, 'etude')
    CACHE_DIR = os.path.join(base.xdg_cache_home, 'etude')
    LOG_DIR = os.path.join(CACHE_DIR, 'log')
    LOCALE_DIR = None

# create missing dirs
for directory in ([CONFIG_DIR, CACHE_DIR, LOG_DIR] + PLUGIN_DIR[1:]):
    if not os.path.isdir(directory):
        try:
            os.makedirs(directory, 0755)
        except OSError:
            print('Can not create %s' % (directory))

#set extraction path for eggs
CLEANUP_FORCE = False
set_extraction_path(CACHE_DIR)
atexit.register(cleanup_resources, CLEANUP_FORCE)

DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')

#entries for plugins
ENTRY_LAYOUTS = "etude.layouts"
ENTRY_PANELS = "etude.panels"
ENTRY_EXTRA = "etude.extra"

#default preferences
default_panels = {
        'Default_Collections-0.1': "Collections",
        "Default_Equalizer-0.1" : "Equalizer",
        "Default_Medialib-0.1" : "Medialib",
        "Default_Playlist-0.1": "Playlist",
        "Default_Songinfo-0.1": "Songinfo",
        "Default_FileBrowser-0.1": "File browser"
        }

default_extras = {
        "Default_Notification-0.1": "Libnotify Notifications",
        "Default_Systray-0.1": "System tray icon"
        }

default_profile = {
        'Profiles': {
            'default': '0'
            },
        'default': {
            "url" : "",
            "music_dir": "",
            "stop_daemon": "0",
            "start_daemon": "0"
            }
        }


class ConnectionProfile(dict):

    """
    Profile of a connection used to 
    - set profile options
    - load profiles
    - create profiles
    """

    def __init__(self, control):
        dict.__init__(self)
        self.control = control
        self.config = control.config
        self.load_config()

    def load(self, profile = None):
        """
        connect to a profile
        """
        if profile in self.profiles:
            success = self.load_profile(profile)
        else:
            success = self.load_default()
            
        if not success:
            self.show_chooser()
        else:
            self.control.init_gui()

    def load_config(self):
        """
        get the saved profiles from the configuration file
        """
        self.config.update_defaults('Profiles', default_profile['Profiles'], exit_on_section = True)
        self.config.update_defaults('Profile_'+'default', default_profile['default'], exit_on_section = True)
        self.profiles = {}
        self.default = None
        for item, value in self.config.section_items('Profiles'):
            self.profiles[item] = bool(int(value))
            if bool(int(value)):
                if self.default != None:
                    self.profiles[item] == False
                else:
                    self.default = item
        self.save_profiles()
    
    def save_profiles(self):
        """save the list of available profiles
        on each line in the configuration there is the name
        of the profile and a number defining the default profile (1)
        """
        for item, value in self.profiles.items():
            self.config.set('Profiles', item, int(value))
        self.config.save_config()

    def save_profile(self, name, dict):
        """
        save a profile
        dict keys:
            default: True|False
            url: ''
            music_dir: ''
        """
        try:
            if name not in self.profiles:
                self.profiles[name] = dict['default']
            else:
                #self.config.set('Profiles', name, int(dict['default']))
                if (self.default != None) and dict['default']:
                    self.profiles[self.default] = False
                self.profiles[name] = dict['default']
            self.save_profiles()
            self.config.set('Profile_'+name, 'url', dict['url'])
            self.config.set('Profile_'+name, 'music_dir', dict['music_dir'])
            self.config.set('Profile_'+name, 'stop_daemon', str(int(dict['stop_daemon'])))
            self.config.set('Profile_'+name, 'start_daemon', str(int(dict['start_daemon'])))

        except KeyError:
            pass
        try:
            if name == self['name']:
                self.update(dict)
        except KeyError:
            #name not set initially
            pass

    def remove_profile(self, name):
        """
        remove a profile
        """
        if name not in self.profiles:
            return
        self.config.remove('Profiles', name)
        self.config.remove('Profile_'+name)
        self.load_config() # to recreate default

    def profile_exists(self, name):
        return name in self.profiles

    def load_profile(self, name):
        """
        get a profile from config and connect to it
        and if set try to start the daemon once
        """
        if name not in self.profiles:
            return False
        self.update(self.get_profile(name))
        result = self.__connect()
        if (result == False) and (self['remote'] == False) and (self['start_daemon'] == True):
            #try to start the daemon automatically
            daemon_res = self.control.start_daemon()
            if daemon_res == False:
                mbox = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
                        _("Unable to start daemon."))
                mbox.run()
                mbox.destroy()
            else:
                result = self.load_profile(self.default)
        return result

    def __connect(self):
        return self.control.client.xmms_connect(url = self['url'], exit_cb = self.control.disconnected)

    def show_chooser(self, result = None, parent = None):
        dialog = gui.ProfileChooser(parent, self.control)
        while True:
            result = dialog.run()
            if result in (gtk.RESPONSE_CLOSE, gtk.RESPONSE_DELETE_EVENT):
                dialog.destroy()
                self.control.quit()
                return
            try_again = dialog.prof_frame.on_load_profile()
            if try_again and (self['remote'] == False):
                if self['start_daemon'] == False:
                    mbox = gtk.MessageDialog(
                        None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO,
                        _("Unable to connect to the XMMS2 daemon.")+" "+_("It may not be running. Attempt to start it?")
                        )
                    mbox.set_title(_("Start daemon?"))
                    mbox.format_secondary_markup(_("This is going to start the \
xmms2 daemon with the default local configuration \
(on unix ~/.config/xmms2/xmms2.conf)\n"))
                    resp = mbox.run()
                    mbox.destroy()
                else:
                    resp = gtk.RESPONSE_YES
                if resp == gtk.RESPONSE_YES:
                    res = self.control.start_daemon()
                    if res == False:
                        mbox = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
                                _("Unable to start daemon."))
                        mbox.run()
                        mbox.destroy()
                    else:
                        try_again = dialog.prof_frame.on_load_profile()
            if try_again:
                #if connecting failed
                dialog.prof_frame.show_connection_error(_("Unable to connect to the XMMS2 daemon."))
            if not try_again:
                break
        self.control.init_gui()
        dialog.destroy()
            
    def get_profile(self, name):
        """
        get a profile from the config file
        """
        dict = {'name': '', 'url': '', 'remote': False, 'music_dir': '', 
                'default': False, 'stop_daemon': False, 'start_daemon': False}
        if name not in self.profiles:
            return dict
        else:
            dict['name'] = name
            dict['url'] = self.config.get('Profile_'+name, 'url')
            dict['default'] = self.profiles[name]
            try:
                dict['stop_daemon'] = bool(int(self.config.get('Profile_'+name, 'stop_daemon')))
            except: #if no stop_daemon found
                pass
            try:
                dict['start_daemon'] = bool(int(self.config.get('Profile_'+name, 'start_daemon')))
            except: #if no stop_daemon found
                pass
            if dict['url'].startswith('unix://') or dict['url'] == '':
                dict['remote'] = False
            elif dict['url'].startswith('tcp://'):
                dict['remote'] = True
            dict['music_dir'] = self.config.get('Profile_'+name, 'music_dir')
            return dict

    def load_default(self):
        """
        load the default profile
        """
        if self.default == None:
            return False
        else:
            return self.load_profile(self.default)

    def is_remote(self):
        """
        returns if the profiles connects to a remote host
        """
        return self['remote']


class Etude(gobject.GObject):
    """
    The main class representing the application
    """

    def __init__(self):
        self.__gobject_init__()
        self.config = config.Config(self)
        self.interfaces = {}
        self.uimanager = None
        self.actions = None
        
        self.client = client.MClient()
        self.profile = ConnectionProfile(self)
        self.profile.load()

    def init_gui(self):
        """
        init the gui: load plugins, show the loaded ones
        """
        if hasattr(self, 'gui_inited'):
            self.unload_plugins()
            self.unload_layout()
        else:
            gui.force_image_on_button_settings()
            img.register_stock_icons()
            icth = gtk.icon_theme_get_default()
            gtk.window_set_default_icon_list(
                    icth.load_icon('etude', 16, 0),
                    icth.load_icon('etude', 32, 0),
                    icth.load_icon('etude', 48, 0),
                    icth.load_icon('etude', 64, 0),
                    icth.load_icon('etude', 128, 0)
                    )

        self.uimanager = gtk.UIManager()
        self.actions = actions.Actions(self)

        self.loaded = {}
        self.find_plugins()
        self.load_layout()

        mw = self.layout.get_main_window()
        mw.add_accel_group(self.uimanager.get_accel_group())

        self.load_panels()
        self.load_extras()
        self.gui_inited = True
        self.client.register_cbs()
        self.emit("g_init_done")

    def quit(self, force_daemon_stop = False):
        """
        quit the whole application
        save the config file
        Quit the daemon if set in the current profile
        """
        try:
            self.config.save_config()
            try:
                self.actions.save()
            except AttributeError:
                pass
            gtk.main_quit()
        except RuntimeError:
            pass
        try:
            if ((self.profile['stop_daemon'] or force_daemon_stop)
                    and self.client._disconnected == False):
                res = self.client.xmms_fast.quit()
                res.wait()
        except:
            print(sys.exc_info())
        cleanup_resources(CLEANUP_FORCE) #cleanup the resources
        sys.exit(0)

    def disconnected(self, xmms= None, profile = None):
        """
        Called manually or when the daemon closes the connection/dies
        disconnect from the current profile:
         - unload xmms plugins
         - disconnect from the daemon
         - unload plugins
         - load the profile from the argument if set
            otherwise show the profile chooser
        """
        self.client.plugins.unload()
        self.client.xmms_disconnect()
        self.unload_plugins(config = False)
        self.unload_layout( config = False)
        try:
            del self.actions
        except AttributeError:
            pass
        try:
            del self.uimanager
        except AttributeError:
            pass
        try:
            self.pref_window.destroy()
        except AttributeError:
            pass

        del self.client
        #this isn't enough, since client is singleton
        del client.MClient._instance #this should do it
        self.client = client.MClient()
        if profile:
            self.profile.load(profile)
        else:
            self.profile.show_chooser()
        #self.client.emit("e_disconnected")

    def start_daemon(self, args = []):
        """
        by Chad J. Schroeder  python daemon big thanks
        a few modifications
        """
        UMASK = 0
        WORKDIR = "/"
        MAXFD = 1024
        """
        if (hasattr(os, "devnull")):
            REDIRECT_TO = os.devnull
        else:
            REDIRECT_TO = "/dev/null"
        """
        REDIRECT_TO = os.path.join(LOG_DIR, "xmms2d.log")
        
        #lets open a pipe, so we now if the doemon was started
        #see xmms2d --status-fd
        try:
            st_read, st_write = os.pipe()
        except OSError,e:
            raise Exception, "%s [%d]" % (e.strerror, e.errno)

        try:
            pid = os.fork()
        except OSError, e:
            raise Exception, "%s [%d]" % (e.strerror, e.errno)

        if pid == 0: #first child
            os.setsid() #new session
            
            try:
                pid = os.fork()
            except OSError, e:
                raise Exception, "%s [%d]" % (e.strerror, e.errno)

            if pid == 0: # the second child
                os.chdir(WORKDIR)
                #os.umask(UMASK) - should write with user permissions
            else:
                os._exit(0) #first child dead - reparent to init
        else: #the main function
            #let's close the pipe for writing
            os.close(st_write)
            char = ''
            try:
                char = os.read(st_read, 1)    
            except OSError, e:
                import errno
                print('Exception reading status file')
                if e.errno != errno.EINTR: 
                    return False
            print("daemon successfully started")
            return True

        import resource
        maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
        if (maxfd == resource.RLIM_INFINITY):
            maxfd = MAXFD

        #iterate through and close all file descriptors
        for fd in range(0, maxfd):
            try:
                if fd != st_write: #don't close the pipe
                    os.close(fd)
            except OSError: #fd wasn't open -> ignore
                pass

        logfd = os.open(REDIRECT_TO, os.O_RDWR|os.O_CREAT|os.O_APPEND)

        os.dup2(logfd, 1)
        os.dup2(logfd, 2)
        #hipphopp - start daemon
        os.execvp('xmms2d', ['xmms2d', '--status-fd='+str(st_write)]+args)
        sys.exit(1)

    def find_plugins(self):
        """
        find plugins using setuptools
        setuptools makes it real easy
        """
        self.layouts = []
        self.available_panels = {}
        self.available_extras = {}
        print(PLUGIN_DIR)
        distributions, errors = working_set.find_plugins(Environment(PLUGIN_DIR))
        map(working_set.add, distributions)  # add plugins+libs to sys.path
        print "Couldn't load", errors        # display errors
        for egg in working_set:
            egg.activate()
            for name in egg.get_entry_map(ENTRY_LAYOUTS):
                entry_point = egg.get_entry_info(ENTRY_LAYOUTS, name)
                cls = entry_point.load()
                self.layouts.append(cls)
                print("layout found", cls)
            for name in egg.get_entry_map(ENTRY_PANELS):
                entry_point = egg.get_entry_info(ENTRY_PANELS, name)
                cls = entry_point.load()
                self.available_panels[cls._unique] = cls
                print("panel found", cls)
            for name in egg.get_entry_map(ENTRY_EXTRA):
                entry_point = egg.get_entry_info(ENTRY_EXTRA, name)
                cls = entry_point.load()
                self.available_extras[cls._unique] = cls
                print("extra found", cls)

    def load_plugin(self, cls, section, emit=True):
        """
        checks if plugin is loaded, if the same plugin with a lower version number is 
        loaded it unloads it, changes configuration
        @return if the plugin is loaded it returns True
        """
        if cls._unique in self.loaded[section]:
            return False
        #check for loaded same version
        shnames = {}
        for inst in self.loaded[section].values():
            shnames[inst._short_name] = inst._unique
        if cls._short_name in shnames:
            self.unload_plugin(shnames[cls._short_name], section)
        #check config
        conf_shnames = {}
        for uniq, shname in self.config.section_items(section):
            conf_shnames[shname] = uniq
        if cls._short_name in conf_shnames:
            self.config.remove(section, conf_shnames[cls._short_name])

        self.config.set(section, cls._unique, cls._short_name)
        plugin = cls(self)
        #add interfaces
        [self.add_interface(*args) for args in plugin._interfaces.iteritems()]
        self.loaded[section][cls._unique] = plugin
        if emit: self.emit("g_plugin_loaded", cls._unique)
        return True


    def load_panel(self, unique, emit = True):
        """
        load a plugin of a plugin type 
         - check the layout, if the panel fits in there 
        """
        cls = self.available_panels[unique]
        #check if panel fits in the layout
        if (self.layout.slots_count[cls._type]-1) < 0 :
            return False
        if not self.load_plugin(cls, "PluginPanels", emit):
            return False

        self.layout.slots_count[cls._type] -= 1
        if emit: self.emit("g_plugin_panel_loaded", cls._unique)
        return True

    def check_panel(self, unique):
        """
        check if the panel can be loaded - if has a empty slot in the layout
        """
        cls = self.available_panels[unique]
        if (self.layout.slots_count[cls._type]-1) >= 0:
            return (True, [])
        return (False, self.get_panels_by_type(cls._type))
    
    def load_extra(self, unique, emit = True):
        """load a plugin of type extra"""
        cls = self.available_extras[unique]
        if not self.load_plugin(cls, "PluginExtras", emit):
            return False
        if emit: self.emit("g_plugin_extra_loaded", cls._unique)
        return True

    def unload_panel(self, unique, emit = True, config = True):
        """unload a plugin of type plugin, free the slot in the layout"""
        type = self.loaded["PluginPanels"][unique]._type
        self.layout.slots_count[type] += 1
        self.unload_plugin(unique, "PluginPanels", config, emit)
        if emit: self.emit("g_plugin_panel_unloaded", unique)

    def unload_extra(self, unique, emit = True, config = True):
        """unload an extra plugin"""
        self.unload_plugin(unique, "PluginExtras", config, emit)
        if emit: self.emit("g_plugin_extra_unloaded", unique)

    def unload_plugin(self, unique, section, config = True, emit=True):
        """unload a plugin"""
        try:
            plugin = self.loaded[section][unique]
            #remove interfaces
            [self.remove_interface(*args) 
                    for args in plugin._interfaces.iteritems()]
            self.loaded[section][unique].unload()
            del plugin
            del self.loaded[section][unique]
        except:
            pass
        if config:
            self.config.remove(section, unique)
        if emit: self.emit("g_plugin_unloaded", unique)

    def load_panels(self):
        """
        load panels according to the saved configuration
        """
        self.loaded["PluginPanels"] = {}
        self.config.update_defaults("PluginPanels", default_panels, exit_on_section = True)
        
        shnames = {}
        for cls in self.available_panels.values():
            if cls._short_name in shnames:
                if LVer(cls._version) <= LVer(shnames[cls._short_name]._version):
                    continue
            shnames[cls._short_name] = cls
        
        for uniq, shname in self.config.section_items("PluginPanels"):
            if uniq in self.available_panels:
                self.load_panel(uniq)
            elif shname in shnames:
                self.load_panel(shnames[shname]._unique)
            else:
                self.config.remove("PluginPanels", uniq)
        self.config.save_config()

    def load_extras(self):
        """
        load extra plugins according to the saved configuration
        """
        self.loaded["PluginExtras"] = {}
        self.config.update_defaults("PluginExtras", default_extras, exit_on_section = True)

        shnames = {}
        for cls in self.available_extras.values():
            if cls._short_name in shnames:
                if LVer(cls._version) <= LVer(shnames[cls._short_name]._version):
                    continue
            shnames[cls._short_name] = cls
        
        for uniq, shname in self.config.section_items("PluginExtras"):
            if uniq in self.available_extras:
                self.load_extra(uniq)
            elif shname in shnames:
                self.load_extra(shnames[shname]._unique)
            else:
                self.config.remove("PluginExtras", uniq)
        self.config.save_config()

    def load_layout(self, layout_cls = None):
        """
        load the layout
        """
        if layout_cls == None:
            try:
                self.layout = self.layouts[0](self)
            except IndexError:
                print("Error: No layout found!")
                sys.exit(1)
        else:
            self.layout = layout_cls(self)
        self.emit('g_plugin_loaded', self.layout._unique)
        self.emit('g_plugin_layout_loaded', self.layout._unique)

    def unload_layout(self, config = True):
        """
        unload a layout - this means unloading all the plugins
        without saving the changes in the config file
        """
        try:
            plugins = [name for name in self.loaded["PluginPanels"].iterkeys()]
            for panel, inst in plugins:
                self.unload_panel(panel, emit = False, config = config)
            name = self.layout._unique
            self.layout.unload()
            self.layout.destroy()
            del self.layout
            self.layout = None
            self.emit('g_plugin_unloaded', name)
            self.emit('g_plugin_layout_unloaded', name)
        except AttributeError:
            pass

    def unload_plugins(self, config = True):
        """unload all plugins, config defines if the changes are saved in config"""
        panels = [name for name in self.loaded["PluginPanels"].iterkeys()]
        extras = [name for name in self.loaded["PluginExtras"].iterkeys()]
        for panel in panels:
            self.unload_panel(panel, config=config)
        for extra in extras:
            self.unload_extra(extra, config = config)

    def plugin_is_loaded(self, uniq):
        """ checks if a plugin is loaded or not """
        return ( (uniq in self.loaded["PluginPanels"]) 
                or (uniq in self.loaded["PluginExtras"]) )

    def get_plugin(self, uniq):
        """ get a loaded plugin according to it's unique name """
        if uniq in self.loaded["PluginPanels"]:
            return self.loaded["PluginPanels"][uniq]
        elif uniq in self.loaded["PluginExtras"]:
            return self.loaded["PluginExtras"][uniq]
        else:
            return None

    def get_panel_by_type(self, type):
        """
        get the first panel plugin found of a given type
        """
        for inst in self.loaded["PluginPanels"].values():
            if inst._type == type:
                return inst
        return None
    
    def get_panels_by_type(self, type):
        """ get all the panel plugins of a given type"""
        lst = []
        for inst in self.loaded["PluginPanels"].values():
            if inst._type == type:
                lst.append(inst)
        return lst

    def get_client(self):
        return self.client

    def get_data_path(self, name):
        """ returns a path to an image, which is stored in the global data dir """
        return os.path.join(DATA_DIR,name)

    def show_preferences(self):
        """ show the non-modal preferences dialog """
        def _response(dialog, response_id):
            dialog.destroy()
            self.config.save_config()
            self.actions.save()
        self.pref_window = epref.PreferencesWindow(self)
        self.pref_window.connect('response', _response)

    def show_about(self):
        gui.about_dialog()
    
    def add_interface(self, name, func):
        if name in self.interfaces:
            self.interfaces[name].append(func)
        else:
            self.interfaces[name] = [func,]
        self.emit('g_interface_added', name)

    def remove_interface(self, name, func):
        try:
            self.interfaces[name].remove(func)
        except KeyError:
            return
        if len(self.interfaces[name]) == 0:
            del self.interfaces[name]
        self.emit('g_interface_removed', name)

    def use_interface(self, name, data=None):
        """
        returns a list of returned values
        """
        ret_list = []
        if name in self.interfaces:
            for func in iter(self.interfaces[name]):
                if hasattr(func, '__call__'):
                    ret_list.append(func(data))
        return ret_list

    def has_interface(self, name):
        return name in self.interfaces


gobject.type_register(Etude)
gobject.signal_new("g_plugin_loaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_unloaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_panel_loaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_panel_unloaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_layout_loaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_layout_unloaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_extra_loaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_plugin_extra_unloaded", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_init_done", Etude, gobject.SIGNAL_RUN_CLEANUP, gobject.TYPE_BOOLEAN, ())

#to communicate between plugins
#known keys -> tray_icon
gobject.signal_new("g_interface_added", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )
gobject.signal_new("g_interface_removed", Etude, gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_STRING,) )

def main():
    #install the translations
    translations = gettext.translation(GETTEXT_DOMAIN, localedir = LOCALE_DIR, fallback=True)
    translations.install(unicode=True)
    
    #make the imported modules global
    global client, epref, gui, plugins, config, img, actions

    import client #some variables need tranlation
    import img
    import preferences as epref
    import gui
    import plugins
    import config
    import actions

    try:
        opts, args = getopt.getopt(sys.argv[1:], "")
    except getopt.GetoptError:
        usage()
        sys.exit(1)
    tgui = True
    if len(args)>=1:
        tgui = False
    
    if tgui:
        gobject.threads_init()
        try:
            t = Etude()
            gtk.main()
        except KeyboardInterrupt:
            pass
        #even if received keyboard interrupt call cleanup, save_config
        t.quit()

    else:
        """
        for now simply give the stock xmms2 cli client the arguments
        """
        os.execv("xmms2", sys.argv[1:])

